/**
  ******************************************************************************
  * @file           
  * @author         古么宁
  * @brief          10x 协议栈初始化相关
  ******************************************************************************
  *
  * COPYRIGHT(c) 2019 GoodMorning 
  *
  ******************************************************************************
  */
/* Includes ---------------------------------------------------*/
#include <string.h>
#include <stdlib.h> // for size_t

#include "iec10x.h"
#include "iec10x_link.h"
#include "iec10x_period.h"
#include "iec10x_telesgnl.h"
#include "iec10x_telectrl.h"
#include "iec10x_sync.h"
#include "iec10x_event.h"
#include "iec10x_fs.h"
#include "iec10x_debug.h"

#include "../iec/devif.h"

/* Private type  ------------------------------------------------------------*/

typedef void  (*ASDUprofn_t)(iec10x_t * bus , uint8_t * ASDU, int ASDUlen);

/* Private macro ------------------------------------------------------------*/

#define IEC_VERSION  "iec10x version : V1.0.12 "

#define DEFAULT_PERIOD            (15*60*1000) ///< 默认轮询周期，单位毫秒 ms
#define DEFAULT_TXTIMEOUT         (3*1000)     ///< 默认超时时间，单位毫秒 ms
#define DEFAULT_SYNC_PERIOD       (10*60*1000) ///< 默认对时周期，单位毫秒 ms
#define DEFAULT_CTRL_WAIT         (8*1000)     ///< 默认遥控最长等待时间，单位毫秒 ms
#define DEFAULT_T0                (5*1000)     ///< 默认轮询周期，单位毫秒 ms
#define DEFAULT_T1                (15*1000)    ///< 默认超时时间，单位毫秒 ms
#define DEFAULT_T2                (5*1000)     ///< 更新滑动窗口时间
#define DEFAULT_T3                (8*1000)     ///< 默认心跳包时间，单位毫秒 ms
#define DEFAULT_K                 (12)         ///< 默认滑动窗口大小
#define DEFAULT_W                 (8)          ///< 默认滑动窗口更新界限，小于 K
#define DEFAULT_RESENDMAX         (3)          ///< 默认最大重发次数
#define DEFAULT_MAX_LEN           (240)        ///< 默认数据包包长，不包含帧头帧尾

/* Private variables --------------------------------------------------------*/

static struct iec_list iec10x_bus ;
static iec_sys_mutex_t iec10x_mtx ;

/* Global variables ---------------------------------------------------------*/

/**
 * 哈希表，此处由于监听类型最大不过 255 ，因此不做哈希冲突处理
 * 虽然有些位置可能会浪费，但是查表速度快，以空间换时间。
 * 仅能通过 iec101_type_listen() 添加，在 iec10x_protocol_init() 统一注册 
*/
ASDUprofn_t lnhashmap[LISTEN_MAP_SIZE] = {0}; //初始化全部清空


/// 全局时间戳
size_t iec_timestamp = 0;

/* Private function prototypes -----------------------------------------------*/


static void default_puts(uint8_t * info , int len)
{
	iec10x_t * bus = container_of(info,iec10x_t,txbuf[0]);
	iec10x_msg(bus,"Tx.\r\n");
}

static void default_rst(void * iec10xbus)
{
}


/**
  * @brief    作主机时，处理从机发送过来的 ASDU 数据包
  * @author   古么宁 
  * @param    bus      : 总线
  * @param    ASDUrx   : 接收到的 ASDU 数据包
  * @param    ASDUlen  : 接收到的 ASDU 数据包长度
  * @return   don't care
*/
static void iec10xmst_data(iec10x_t * bus,unsigned char * ASDU,int ASDUlen)
{
	IEC_INDEX(bus->causelen,bus->cmnaddrlen);
	int        addrSQ  = ASDU[IEC_QUALIFIER] & (0x80);
	int        datanum = ASDU[IEC_QUALIFIER] & (~0x80);
	uint8_t  * usrdata  , *info = &ASDU[IEC_INFO];
	uint32_t * addrlist , *addrlistbuf ;

	/* 查找设备层交互信息，处理 ASDU 信息结构体 */
	iec_devif_t * dev = iec_devif_find(&bus->device,ASDU[IEC_TYPE]);
	if (NULL == dev || NULL == dev->profn || !datanum) {
		return ;
	}

    /* 申请内存解析当前帧数据，在此函数结尾释放 */
	addrlistbuf = iec_malloc(sizeof(uint32_t) * datanum);
	if (NULL == addrlistbuf) {
		iec10x_err(bus,"not enough memory to parse ASDU.\r\n");
		return ;
	}

	addrlist = addrlistbuf;
	if (addrSQ) { //如果是连续地址
		uint32_t dataaddr ;
		if (bus->infoaddrlen == 3) {
			dataaddr = info[0] | (info[1]<<8) | (info[2]<<16);
			info += 3;
		}
		else {
			dataaddr = info[0] | (info[1]<<8);
			info += 2;
		}
		
		/*越过第一个信息对象地址后面为连续的结构体数据.为每个结构体补全地址标识*/
		usrdata = info ; 
		for (int i = 0; i < datanum ; ++i) {
			*addrlist++ = dataaddr++;
		}
	}
	else {
		uint8_t * copy = info;
		usrdata = info ;
	
		for (int i = 0; i < datanum ; ++i,++addrlist) {
			// 提取当前结构体的信息对象地址
			if (bus->infoaddrlen == 3) {
				*addrlist = info[0] | (info[1]<<8) | (info[2]<<16);
				info += 3;
			}
			else {
				*addrlist = info[0] | (info[1]<<8);
				info += 2;
			}
			
			//不带对象地址的信息体长度
			for (int k = 0 ; k < dev->argsize ; ++k){ 
				*copy++ = *info++;
			}
		}
	}

	dev->profn(bus,ASDU[IEC_COT],datanum,addrlistbuf,usrdata);

    /* 释放内存 */
	iec_free(addrlistbuf);
}


/**
  * @brief    总线对象注册至协议栈
  * @param    bus        : 主机总线对象
  * @return   don't care
*/
static void iec10x_register(struct iec10x *bus)
{
	iec_sys_mutex_lock(&iec10x_mtx);
	iec_list_add_tail(&bus->node,&iec10x_bus);
	iec_sys_mutex_unlock(&iec10x_mtx);
}



/**
  * @brief    总线对象注销，去初始化，释放系统资源
  * @note     此函数将会在 iec10x_periodic_handleic_handle 执行，所以修改链表不加锁
  * @param    bus        : 主机总线对象
  * @return   don't care
*/
static void iec10x_unregister(iec10x_t * bus)
{
	iec_sys_mutex_lock(&iec10x_mtx);
	iec_list_del_init(&bus->node);
	iec_sys_mutex_unlock(&iec10x_mtx);
}



/**
  * @brief    定时轮询
  * @note     此函数需要用户实现周期调用一次，建议 100ms / 50ms / 25ms
  *           此函数的轮询周期也决定了总线的发送频率，即每包的间隔
  * @param    localtime  : 毫秒精度的系统时间戳
  * @return   don't care
*/
void iec10x_periodic_handle(size_t localtime)
{
	static const periodfn_t func[8] = {
		iec104slv_period           ,
		iec104mst_period           ,
		iec101slv_unbalance_period ,
		iec101mst_period           ,
		iec104slv_period           ,
		iec104mst_period           ,
		iec101slv_balance_period   ,
		iec101mst_period           ,
	};
	struct iec_list * pos  ;
	iec10x_t * bus ;
	periodfn_t period ;

	iec_timestamp = localtime;
	
	/* 遍历链表 */
	iec_sys_mutex_lock(&iec10x_mtx);
	for (pos = iec10x_bus.next; pos != (&iec10x_bus); pos = pos->next) {
		bus = container_of(pos,iec10x_t,node);
		period = func[bus->mode & 0x07];
		period(bus);
	}
	iec_sys_mutex_unlock(&iec10x_mtx);
}


/**
  * @brief    断开连接
  * @param    bus : 需要断开连接的总线
  * @return   don't care
*/
void iec10x_disconnect(iec10x_t * bus)
{
	if (bus->mode & IEC_101) {
		iec101_disconnect(bus);
	}
	else {
		iec104_disconnect(bus);
	}
}


/**
  * @brief    总线对象初始化，设定总线模式和硬件输出
  * @param    bus        : 主机总线对象
  * @param    cfg        : 初始化参数
  * @param    devinitfn  : 用户初始化回调函数，比如注册各类数据响应
  * @return   don't care
*/
void iec104_bus_init(iec10x_t * bus,const iec104cfg_t * _cfg , iec_fn_t  devinitfn)
{
	static const iec104cfg_t zero = {0};
	iec104cfg_t * cfg;
	if (!bus) {
		return ;
	}

	memset(bus,0,sizeof(iec10x_t));
	cfg = (_cfg) ? (iec104cfg_t*)_cfg : (iec104cfg_t*)&zero;
	
	bus->printf      = cfg->print  ;
	bus->dbglevel    = cfg->print       ? cfg->dbglevel    : 0                  ;
	bus->haltx       = cfg->haltx       ? cfg->haltx       : default_puts       ;
	bus->halrst      = cfg->halrst      ? cfg->halrst      : default_rst        ;
	bus->data_period = cfg->period      ? cfg->period      : DEFAULT_PERIOD     ;
	bus->sync_period = cfg->sync_period ? cfg->sync_period : DEFAULT_SYNC_PERIOD;
	bus->mode        = cfg->mode ;
	bus->cmnaddrlen  = cfg->cmnaddrlen  ? cfg->cmnaddrlen  : 2                ;
	bus->causelen    = cfg->causelen    ? cfg->causelen    : 2                ;
	bus->infoaddrlen = cfg->infoaddrlen ? cfg->infoaddrlen : 3                ;
	bus->cmnaddr     = cfg->cmnaddr     ? cfg->cmnaddr     : 1                ;
	bus->ctrl_wait   = cfg->ctrl_wait   ? cfg->ctrl_wait   : DEFAULT_CTRL_WAIT;
	bus->t0          = cfg->t0          ? cfg->t0          : DEFAULT_T0       ;
	bus->t1          = cfg->t1          ? cfg->t1          : DEFAULT_T1       ;
	bus->t2          = cfg->t2          ? cfg->t2          : DEFAULT_T2       ;
	bus->t3          = cfg->t3          ? cfg->t3          : DEFAULT_T3       ;
	bus->iec104.k    = cfg->k           ? cfg->k           : DEFAULT_K        ;
	bus->iec104.w    = cfg->w           ? cfg->w           : DEFAULT_W        ;

	if (cfg->pktmax < 16 || cfg->pktmax > DEFAULT_MAX_LEN) {
		bus->pktmax = DEFAULT_MAX_LEN ;
	}

	bus->onlinet = iec_timestamp - bus->t3;

	/* 初始化互斥量 */
	iec_sys_mutex_init(&bus->mtx);
	init_iec_list(&bus->event);

	/* 总线加入协议栈 */
	iec10x_register(bus);

	if (devinitfn) {
		devinitfn(bus);
	}
}


/**
  * @brief    总线对象初始化，设定总线模式和硬件输出
  * @param    bus        : 主机总线对象
  * @param    cfg        : 初始化参数
  * @param    devinitfn  : 用户初始化回调函数，比如注册各类数据响应
  * @return   don't care
*/
void iec101_bus_init(iec10x_t * bus,const iec101cfg_t * _cfg , iec_fn_t  devinitfn)
{
	static const iec101cfg_t zero = {0};
	iec101cfg_t * cfg;
	if (!bus) {
		return ;
	}

	memset(bus,0,sizeof(iec10x_t));
	cfg = (_cfg) ? (iec101cfg_t*)_cfg : (iec101cfg_t*)&zero;

	bus->printf      = cfg->print  ;
	bus->dbglevel    = cfg->print       ? cfg->dbglevel    : 0                  ;
	bus->haltx       = cfg->haltx       ? cfg->haltx       : default_puts       ;
	bus->halrst      = cfg->halrst      ? cfg->halrst      : default_rst        ;
	bus->data_period = cfg->period      ? cfg->period      : DEFAULT_PERIOD     ;
	bus->sync_period = cfg->sync_period ? cfg->sync_period : DEFAULT_SYNC_PERIOD;
	bus->mode        = cfg->mode ;
	bus->cmnaddrlen  = cfg->cmnaddrlen  ? cfg->cmnaddrlen  : 2                ;
	bus->causelen    = cfg->causelen    ? cfg->causelen    : 2                ;
	bus->infoaddrlen = cfg->infoaddrlen ? cfg->infoaddrlen : 3                ;
	bus->cmnaddr     = cfg->cmnaddr     ? cfg->cmnaddr     : 1                ;
	bus->ctrl_wait   = cfg->ctrl_wait   ? cfg->ctrl_wait   : DEFAULT_CTRL_WAIT;
	bus->t0          = DEFAULT_T0 ;
	bus->t1          = cfg->txtimeout   ? cfg->txtimeout   : DEFAULT_T1       ;
	bus->t2          = 0 ;
	bus->t3          = cfg->heartbeat   ? cfg->heartbeat   : (DEFAULT_T3/4)   ;

	bus->iec101.lnaddr    = cfg->lnkaddr    ? cfg->lnkaddr    : 1             ;
	bus->iec101.lnaddrlen = cfg->lnkaddrlen ? cfg->lnkaddrlen : 2             ;
	bus->iec101.resendmax = cfg->resendmax  ? cfg->resendmax  : 3             ;

	if (cfg->pktmax < 16 || cfg->pktmax > DEFAULT_MAX_LEN) {
		bus->pktmax = DEFAULT_MAX_LEN ;
	}
	
	if (cfg->balance) {
		bus->mode |= IEC_BALANCE;
	}
	if (cfg->direct) {
		bus->mode |= 0x80;
	}

	bus->mode |= IEC_101 ;
	bus->onlinet = iec_timestamp - bus->t3;

	/* 初始化互斥量 */
	iec_sys_mutex_init(&bus->mtx);
	init_iec_list(&bus->event);

	/* 总线加入协议栈 */
	iec10x_register(bus);

	if (devinitfn) {
		devinitfn(bus);
	}
}




/**
  * @brief    总线对象去初始化，释放总线状态及其内存
  * @param    bus        : 主机总线对象
  * @return   don't care
*/
int iec10x_bus_deinit(iec10x_t * bus)
{
	if (bus->node.next && bus->node.prev && bus->node.prev != bus->node.next){
		iec10x_unregister(bus);
		iec10x_event_deinit(bus);
		iec_device_destroy(&bus->device);
		iec_sys_mutex_deinit(&bus->mtx);
	}
	return 0;
}


/**
  * @brief    加入监听链表，不直接调用，
  *           通过 iec101.c 的宏定义 iec101_type_listen 调用
  * @param    type       : 监听类型
  * @param    ASDUprofn  : 当前类型的处理函数
  * @return   
*/
int iec10x_type_listen(int type ,ASDUprofn_t ASDUprofn) 
{
	#if (LISTEN_MAP_SIZE < 128)
	if (type > 127) { // 仅监听标准的 128 以内数据包，超过 128 ，请加大 lnhashmap 内存
		IEC_MSG("error:%s(),%d\r\n",__FUNCTION__,__LINE__);
		IEC_MSG("仅监听标准的 128 以内数据包，超过 128 ，请加大 lnhashmap 内存\r\n");
		return -1;
	}
	#endif

	if (lnhashmap[type] != NULL) {
		IEC_MSG("error:%s(),%d\r\n",__FUNCTION__,__LINE__);
		IEC_MSG("error,this type[0x%x,%d] is listened\r\n",type,type);
		return -1;
	}

	lnhashmap[type] = ASDUprofn ; // 加入表
	return 0 ;
}


/**
  * @brief    总线接收数据并解析
  * @param    bus   : 需要解析的总线结构体 
  * @param    data  : 包数据 
  * @param    len   : 包数据长度
  * @return   don't care
*/
void iec10x_recv(iec10x_t * bus , uint8_t * data , int len)
{
	if (bus->mode & IEC_101) {
		iec101_protocol_recv(bus, data, len);
	}
	else {
		iec104_protocol_recv(bus, data, len);
	}
}

/**
  * @brief    用户设备接口管理注册
  * @param    bus      : 目标总线
  * @param    type     : 当前数据接口的类型
  * @param    statfn   : 信息结构体状态获取接口
  * @param    getfn    : 信息体结构体内容获取接口
  * @param    profn    : 信息体结构体内容处理接口
  * @return   成功返回 0 ，否则返回其他
*/
int iec10x_devif_register(
	iec10x_t *         bus       ,
	unsigned char      type      ,
	dev_statfn_t       statfn    ,
	dev_getfn_t        getfn     ,
	dev_profn_t        profn   ) 
{
	int res = iec_devif_register(&bus->device, type, statfn, getfn ,profn);
	if (res < 0 && bus->printf) {
		bus->printf("\r\n<!>:register interface(%d) failed.\r\n",type);
	}
	return res;
}

/**
  * @brief    协议栈初始化，生成响应链表
  * @param    void
  * @return   don't care
*/
void iec10x_protocol_init(void)
{
	IEC_MSG("\r\n\t"IEC_VERSION" %s,%s\r\n\r\n",__DATE__,__TIME__);

	iec_sys_mutex_init(&iec10x_mtx);
	init_iec_list(&iec10x_bus);

	// 主从
	iec10x_type_listen(C_IC_NA_1,iec10x_summon_respond);      //监听总召唤，一般为从机接收
	iec10x_type_listen(C_CS_NA_1,synchronize_respond);        //监听时间同步命令，一般为从机接收

	iec10x_type_listen(C_SC_NA_1,iec10x_telectrl_respond);    //监听遥控命令，单点
	iec10x_type_listen(C_DC_TA_1,iec10x_telectrl_respond);    //监听遥控命令，单点
	iec10x_type_listen(C_DC_NA_1,iec10x_telectrl_respond);    //监听遥控命令，双点
	iec10x_type_listen(C_RC_NA_1,iec10x_telectrl_respond);    //监听遥控命令，步调节
	iec10x_type_listen(C_SE_NA_1,iec10x_telectrl_respond);    //监听设置归一化值命令 
	iec10x_type_listen(C_SE_NB_1,iec10x_telectrl_respond);    //监听设置标度化值命令
	iec10x_type_listen(C_SE_NC_1,iec10x_telectrl_respond);    //监听设置短浮点数命令
	iec10x_type_listen(C_BO_NC_1,iec10x_set32bit);            //监听设置 32 比特串命令
	
	#if 0
		iec10x_type_listen(C_CI_NA_1,allpower_respond);    //监听累计电度命令，一般为从机接收
	#endif 

	// 主机相关 
	iec10x_type_listen(M_EI_NA_1,iec10xmst_data);//监听 从机初始化接收报文

	// SIQ
	iec10x_type_listen(M_SP_NA_1,iec10xmst_data);//监听 SIQ 接收，一般为主机接收
	iec10x_type_listen(M_SP_TA_1,iec10xmst_data);//监听带 3 字节时标 SIQ 接收，一般为主机接收
	iec10x_type_listen(M_SP_TB_1,iec10xmst_data);//监听带 CP56Time2a 时标的 SIQ 接收，一般为主机接收

	// DIQ 
	iec10x_type_listen(M_DP_NA_1,iec10xmst_data);//监听 DIQ 接收，一般为主机接收
	iec10x_type_listen(M_DP_TA_1,iec10xmst_data);//监听带 3 字节时标 DIQ 接收，一般为主机接收
	iec10x_type_listen(M_DP_TB_1,iec10xmst_data);//监听带 CP56Time2a 时标的 DIQ 接收，一般为主机接收

	// VTI 
	iec10x_type_listen(M_ST_NA_1,iec10xmst_data);//监听 VTI 接收，一般为主机接收
	iec10x_type_listen(M_ST_TA_1,iec10xmst_data);//监听带 3 字节时标 VTI 接收，一般为主机接收
	iec10x_type_listen(M_ST_TB_1,iec10xmst_data);//监听带 CP56Time2a 时标的 VTI 接收，一般为主机接收

	//  NVA 归一化值相关监听 
	iec10x_type_listen(M_ME_NA_1,iec10xmst_data);//监听不带时标的 NVA 接收，一般为主机接收
	iec10x_type_listen(M_ME_TA_1,iec10xmst_data);//监听带 CP24Time2a 时标的 NVA 接收，一般为主机接收
	iec10x_type_listen(M_ME_TD_1,iec10xmst_data);//监听带 CP56Time2a 时标的 NVA 接收，一般为主机接收
	iec10x_type_listen(M_ME_ND_1,iec10xmst_data);//监听不带时标不带品质描述词的 NVA 接收，一般为主机接收

	//  SVA 标度化值相关监听
	iec10x_type_listen(M_ME_NB_1,iec10xmst_data);//监听不带时标的 SVA 接收，一般为主机接收
	iec10x_type_listen(M_ME_TB_1,iec10xmst_data);//监听带 CP24Time2a 时标的 SVA 接收，一般为主机接收
	iec10x_type_listen(M_ME_TE_1,iec10xmst_data);//监听带 CP56Time2a 时标的 SVA 接收，一般为主机接收

	//  STD 标度化值相关监听
	iec10x_type_listen(M_ME_NC_1,iec10xmst_data);//监听不带时标的 STD 接收，一般为主机接收
	iec10x_type_listen(M_ME_TC_1,iec10xmst_data);//监听带 CP24Time2a 时标的 STD 接收，一般为主机接收
	iec10x_type_listen(M_ME_TF_1,iec10xmst_data);//监听带 CP56Time2a 时标的 STD 接收，一般为主机接收

	// 主站接收继电保护相关事件
	iec10x_type_listen(M_EP_TA_1,iec10xmst_data);//带时标的继电保护装置事件 
	iec10x_type_listen(M_EP_TD_1,iec10xmst_data);//监听带 CP56Time2a 时标的继电保护装置事件接收，一般为主机接收

	iec10x_type_listen(M_EP_TB_1,iec10xmst_data);//监听带时标的继电保护装置成组启动事件接收，一般为主机接收
	iec10x_type_listen(M_EP_TE_1,iec10xmst_data);//监听带 CP56Time2a 时标的继电保护装置成组启动事件接收，一般为主机接收

	iec10x_type_listen(M_EP_TC_1,iec10xmst_data);//监听带时标的继电保护装置成组输出电路信息接收，一般为主机接收
	iec10x_type_listen(M_EP_TF_1,iec10xmst_data);//监听带 CP56Time2a 时标的继电保护装置成组输出电路信息接收，一般为主机接收

	// 32BIT 比特串相关事件  
	iec10x_type_listen(M_BO_NA_1,iec10xmst_data);//监听不带时标的 BSI 接收，一般为主机接收
	iec10x_type_listen(M_BO_TA_1,iec10xmst_data);//监听带 CP24Time2a 时标的 BSI 接收，一般为主机接收
	iec10x_type_listen(M_BO_TB_1,iec10xmst_data);//监听带 CP56Time2a 时标的 BSI 接收，一般为主机接收

	// 32BIT 比特串相关事件      
	iec10x_type_listen(M_IT_NA_1,iec10xmst_data);//监听不带时标的 BSI 接收，一般为主机接收
	iec10x_type_listen(M_IT_TA_1,iec10xmst_data);//监听带 CP24Time2a 时标的 BSI 接收，一般为主机接收
	iec10x_type_listen(M_IT_TB_1,iec10xmst_data);//监听带 CP56Time2a 时标的 BSI 接收，一般为主机接收

	// 带变位检出的成组单点信息
	iec10x_type_listen(M_PS_NA_1,iec10xmst_data);//监听 SCD 接收，一般为主机接收

	#if ENABLE_FILE_TRANS
		iec10x_type_listen(0xD2,iec10x_file_service);
	#endif

	iec10x_parser_init();
}

